#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>

enum
{
	HAMMING_INFO		= 1U << 0,
	HAMMING_WARNING		= 1U << 1,
	HAMMING_DEBUG		= 1U << 2,
	HAMMING_ERROR		= 1U << 3,
};
 
int g_log_level = HAMMING_INFO | HAMMING_DEBUG | HAMMING_WARNING | HAMMING_ERROR;

#define hamming_print(level, fmt, ...) do {\
	if (level & g_log_level) {\
		printf("[" #level "] [%s %d] : "fmt"\n", __func__, __LINE__,  ##__VA_ARGS__);\
	}\
} while (0)


unsigned short table[4] = { 0x02AA, 0x0266, 0x00E1, 0x000F };
unsigned short not_table[4] = { 0x0D55, 0x0D99, 0xFF1E, 0xFFF0 };

void print_serialize_data(char ch) {
	int i = 0;
	int tmp[8] = {0};
	int val = 0;

	for (i = 0; i < 8; i++) {
		val = (ch >> i) & 0x1;\
		tmp[8 - 1 - i] = val;
	}

	for (i = 0; i < 8; i++) {
		printf("%d", tmp[i]);
	}
	printf("\n");
}

void print_unserialize_data(short encode_data) {
	int i = 0;
	int tmp[12] = {0};
	int val = 0;

	for (i = 0; i < 12; i++) {
		val = (encode_data >> i) & 0x1;\
		tmp[12 - 1 - i] = val;
	}

	for (i = 0; i < 12; i++) {
		printf("%d", tmp[i]);
	}
	printf("\n");
}

#define _ENCODE_LEN 12
short* hamming_correction(char *unserialize_encode_data, int msg_len) {
	int _i = 0;
	int i = 0;
	int j = 0;
	int k = 0;
	int res = 0;
	short *data = NULL;

	if (NULL == (data = (short*)malloc(msg_len / 12 * sizeof(short)))) {
		printf("malloc failed.\n");
		goto exit;
	}

	for (_i = 0; _i < msg_len / 12; _i++) {
		int correction_code = 0xFFFF;
		char encode[_ENCODE_LEN + 1] = {'\0'};
		uint8_t verify_code[4] = {0};
		int err_cnt = 0;
		int parity_idx = 1;
		data[_i] = 0;

		memset(encode, 0, sizeof(encode));
		memcpy(encode, unserialize_encode_data, _ENCODE_LEN);
	
		unserialize_encode_data += _ENCODE_LEN;

		//printf("--------------------\n");
		for (i = 0; i < 12; i++) {
			//printf("%d", encode[12 - i - 1]);
			data[_i] |= (encode[12 - i - 1] << i);
		}
		//printf("\n");
		//print_unserialize_data(data[_i]);
		//continue;

		for (i = 0, j = 1; i < 4 && j <= 8; i++, j *= 2) {	// 4个校验位
			res = 0;
			for (k = j; k < 12; k++) {
				if (j & (k + 1)) {
					res ^= (encode[k]);
				}
			}

			if (res != encode[j - 1]) {
				//printf("[%d] %d != %d\n", i, res, encode[j - 1]);
				verify_code[i] = 1;
				err_cnt++;
			}
		}

		if (1 == err_cnt) {
			printf("\n  error : ");
			print_unserialize_data(data[_i]);
			for (i = 0; i < 4; i++) {
				//printf("(%d) [%d] = %d\n", err_cnt, i , verify_code[i]);
				if (verify_code[i] != 0) {
					data[_i] ^= (1 << (12 - parity_idx));
				}
				parity_idx *= 2;
			}
			printf("correct : ");
			print_unserialize_data(data[_i]);
		} else if (err_cnt > 1) {
			printf("\n  error : ");
			print_unserialize_data(data[_i]);
			for (i = 0; i < 4; i++) {
				//printf("(%d) [%d] = %d\n", err_cnt, i , verify_code[i]);
				if (verify_code[i] != 0) {
					correction_code &= table[i];
				} else if (verify_code[i] == 0) {
					correction_code &= not_table[i];
				}
			}
			data[_i] ^= correction_code;
			printf("correct : ");
			print_unserialize_data(data[_i]);
		}
		
		//print_unserialize_data(data[_i]);

		//printf("data[%d] = %#x\n", _i, data[_i]);
	}
exit:
	return data;
}

void asciitobin(char *bin, int asc)
{
	int i = 0;
	int r = 0;

	for (i = 0; i < 8; i++){
		r = asc % 2;
		asc = asc / 2;
		if(0 == r) {
			bin[8 - i - 1] = '0';
		} else {
			bin[8 - i - 1] = '1';
		}
	}
}

// 用完记得释放内存
char* hamming_encode(char *raw_data, int len) {
	int i = 0;
	int j = 0;
	int count = 0;
	int offset = 0;
	int bit = 0;
	char *message = NULL;
	char *encodedmsg = NULL;
	int msgbit = 0;

	if (NULL == (message = (char *)malloc(len * 8 + 1))
			|| NULL == (encodedmsg = (char *)malloc(12 * len + 1))) {
		hamming_print(HAMMING_ERROR, "malloc failed.");
		goto exit;
	}

	message[len * 8] = '\0';
	for (i = 0; i < len; i++){
		asciitobin(message + (i * 8), (int)raw_data[i]);
	}

	encodedmsg[12 * len] = '\0';	//Insert all the bits and set parity bits to -1
	for (i = 0; i < len; i++) {		//For each character
		msgbit = 0;
		for (j = 0; j < 12; j++) {
			if (8 % (j + 1) != 0) {
				encodedmsg[i * 12 + j] = message[i * 8 + msgbit];
				msgbit++;
			} else {
				encodedmsg[i * 12 + j] = 'p'; //For now let the parity bits be p.
			}
		}
	}

	for (i = 0; i < len; i++) {
		for (j = 1; j <= 8; j *= 2) {	// 4个校验位
			int k = 0;
			int tmp = 0;
			for (k = j; k < 12; k++) {
				if (j & (k + 1)) {
					tmp ^= (encodedmsg[i * 12 + k] - '0');
				}
			}
			//printf(" >> %c.\n", tmp + '0');
			encodedmsg[i * 12 + j - 1] = tmp + '0';
		}
	}

exit:
	return encodedmsg;
}


char* hamming_decode(short *encode_data, int data_len) {
	int i = 0;
	int j = 0;
	int k = 0;
	int count = 0;
	int offset = 0;
	int bit = 0;
	char *decode_data = NULL;
	char tmp = 0;
	int lowbit = 0;

	if (NULL == (decode_data = (char *)malloc(data_len + 1))) {
		hamming_print(HAMMING_ERROR, "malloc failed.");
		goto exit;
	}

	memset(decode_data, 0, data_len + 1);

	decode_data[data_len] = '\0';
	for (i = 0; i < data_len; i++) {
		tmp = 0;
		k = 0;
		//printf("[%d] = %#x\n", i, encode_data[i] );
		for (j = 0; j < 12; j++) {
			lowbit = (encode_data[i] >> j) & 0x1;
			if (j != 4 && j != 8
				&& j != 10 && j != 11) {
				tmp |= lowbit << k;
				//printf("j = %d, k = %d : ", j, k);
				//print_serialize_data(tmp);
				k++;
			}
		}

		decode_data[i] = tmp;

		//printf("raw data = %c, ", tmp);
		//print_serialize_data(tmp);
	}

exit:
	return decode_data;
}


char* _12bits_serialize(char* _12bits_data, int raw_data_len) {
	int i = 0;
	int j = 0;
	int ch_count = 0;
	char* char_buf = NULL;
	int data_len = 0;

	data_len = raw_data_len * 12;
	ch_count = data_len / 8 + (data_len % 8 ? 1 : 0);

	printf("malloc %d char buffers.\n", ch_count);

	if (NULL == (char_buf = (char*)malloc(ch_count + 1))) {
		printf("malloc %d char buffers failed.\n", ch_count);
		goto exit;
	}

	char_buf[ch_count] = '\0';
	int idx = 0;
	for (i = ch_count - 1; i >= 0; i--) {
		char_buf[i] = 0;
		for(j = 0; j < 8; j++) {
			idx = --data_len;
			if (idx < 0) {
				//printf(">>> [%d] %d << %d\n", idx, 0, j);
				char_buf[i] |= 0 << j;
			} else {
				//printf(">>> [%d] %d << %d\n", idx, src[idx] - '0', j);
				char_buf[i] |= (_12bits_data[idx] - '0') << j;
			}
		}
		// printf("%#x.\n", char_buf[i]);
	}

exit:
	return char_buf;
}

char* _8bits_unserialize(char* _8bits_data) {
	int i = 0;
	int j = 0;
	int idx = 0;
	int _8bits_data_cnt = 0;
	int unserialize_data_cnt = 0;
	int unserialize_data_len = 0;
	char* unserialize_data = NULL;

	_8bits_data_cnt = strlen(_8bits_data);
	unserialize_data_cnt = 8 * _8bits_data_cnt / 12;
	unserialize_data_len = unserialize_data_cnt * 12;

	printf("8bits data cnt = %d.\n", unserialize_data_cnt);

	if (NULL == (unserialize_data = (char*)malloc(unserialize_data_len))) {
		printf("malloc unserialize_data failed.\n");
		goto exit;
	}

	idx = unserialize_data_len;
	for (i = _8bits_data_cnt - 1; i >= 0 ; i--) {
		for (j = 0; j < 8; j++) {
			--idx;
			if (idx < 0) { break; }
			unserialize_data[idx] = (_8bits_data[i] >> j) & 0x1;

		}
	}

exit:
	return unserialize_data;
}


char * encode_to(char *input)
{
    int len = 0;
	int i;
    len = strlen(input);
	char * encoded = hamming_encode(input, len); 
	return _12bits_serialize(encoded, len);  
}

